﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace InSys.Documents
{
    public class CacheObjectItem
    {
        private document[] table;
        private static Dictionary<int, string> sequence; // последовательность представления элементов в кэше данных
        private static bool dictionary_filled = false;

        public static void FillSequence(IEnumerable<string> string_sequence)
        {
            int i = 0;
            sequence = new Dictionary<int, string>();
            foreach (string str in string_sequence)
            {
                sequence[i++] = str.Trim().ToUpper();
            }
            dictionary_filled = true;
        }

        public CacheObjectItem()
        {
        }

        public CacheObjectItem(IEnumerable<document> source_data)
        {
            int i = 0;
            table = new document[source_data.Count()];
            foreach (document item in source_data)
            {
                table[i++] = item;
            }
        }

        public CacheObjectItem(IEnumerable<document> source_data, IEnumerable<string> string_sequence)
            : this(source_data)
        {
            System.Diagnostics.Debug.Assert(null != string_sequence);
            FillSequence(string_sequence);
        }

        public object GetItemAtRowCol(int rowIndex, int colIndex)
        {
            object result = null;
            document item = null;
            System.Diagnostics.Debug.Assert(true == dictionary_filled);
            item = table[rowIndex];
            string propName = sequence[colIndex]; // Получаем наименование свойства
            // Возвращаем объект, соответствующий свойству
            if (propName == "ID_DOCUMENT")
            {
                result = item.id_document;
            }
            else if (propName == "LOCAL_ID")
            {
                result = item.local_id;
            }
            return result;
        }
    }

    public interface IDataPageRetriever
    {
        CacheObjectItem SupplyPageOfData(int lowerPageBoundary, int rowsPerPage);
        int RowCount { get; }
    }

    public class DataRetriever : IDataPageRetriever
    {
        private int rowCountValue = -1;
        private MapTestLinqDataContext db = null;
        private IEnumerable<string> columnsOrder = null;

        public DataRetriever(MapTestLinqDataContext db)
        {
            this.db = db;
        }

        public DataRetriever(MapTestLinqDataContext db, IEnumerable<string> columnsOrder)
            : this(db)
        {
            this.columnsOrder = columnsOrder;
        }

        public int RowCount
        {
            get
            {
                if (rowCountValue != -1)
                {
                    return rowCountValue;
                }
                rowCountValue = db.document.Count();
                return rowCountValue;
            }
        }

        CacheObjectItem IDataPageRetriever.SupplyPageOfData(int lowerPageBoundary, int rowsPerPage)
        {
            IEnumerable<document> query = (from item in db.document orderby item.id_document select item).Skip(lowerPageBoundary).Take(rowsPerPage);
            CacheObjectItem memobject = new CacheObjectItem(query, columnsOrder);
            return memobject;
        }
    }

    class CacheObject
    {
        private static int RowsPerPage;

        // Represents one page of data.  
        public struct DataPage
        {
            public CacheObjectItem table;
            private int lowestIndexValue;
            private int highestIndexValue;

            public DataPage(CacheObjectItem table, int rowIndex)
            {
                this.table = table;
                lowestIndexValue = MapToLowerBoundary(rowIndex);
                highestIndexValue = MapToUpperBoundary(rowIndex);
                System.Diagnostics.Debug.Assert(lowestIndexValue >= 0);
                System.Diagnostics.Debug.Assert(highestIndexValue >= 0);
            }

            public int LowestIndex
            {
                get
                {
                    return lowestIndexValue;
                }
            }

            public int HighestIndex
            {
                get
                {
                    return highestIndexValue;
                }
            }

            public static int MapToLowerBoundary(int rowIndex)
            {
                // Return the lowest index of a page containing the given index.
                return (rowIndex / RowsPerPage) * RowsPerPage;
            }

            private static int MapToUpperBoundary(int rowIndex)
            {
                // Return the highest index of a page containing the given index.
                return MapToLowerBoundary(rowIndex) + RowsPerPage - 1;
            }
        }

        private DataPage[] cachePages;
        private IDataPageRetriever dataSupply;

        public CacheObject(IDataPageRetriever dataSupplier, int rowsPerPage)
        {
            dataSupply = dataSupplier;
            CacheObject.RowsPerPage = rowsPerPage;
            //CacheObjectItem.FillSequence(sequence);
            LoadFirstTwoPages();
        }

        public int Count()
        {
            return dataSupply.RowCount;
        }

        // Sets the value of the element parameter if the value is in the cache.
        private bool IfPageCached_ThenSetElement(int rowIndex,
            int columnIndex, ref object element)
        {
            if (IsRowCachedInPage(0, rowIndex))
            {
                element = (cachePages[0].table.GetItemAtRowCol(rowIndex % RowsPerPage, columnIndex));
                return true;
            }
            else if (IsRowCachedInPage(1, rowIndex))
            {
                element = (cachePages[1].table.GetItemAtRowCol(rowIndex % RowsPerPage, columnIndex));
                return true;
            }

            return false;
        }

        public object RetrieveElement(int rowIndex, int columnIndex)
        {
            object element = null;

            if (IfPageCached_ThenSetElement(rowIndex, columnIndex, ref element))
            {
                return element;
            }
            else
            {
                return RetrieveData_CacheIt_ThenReturnElement(
                    rowIndex, columnIndex);
            }
        }

        private void LoadFirstTwoPages()
        {
            cachePages = new DataPage[]{
                new DataPage(dataSupply.SupplyPageOfData(
                    DataPage.MapToLowerBoundary(0), RowsPerPage), 0), 
                new DataPage(dataSupply.SupplyPageOfData(
                    DataPage.MapToLowerBoundary(RowsPerPage), 
                    RowsPerPage), RowsPerPage)};
        }

        private object RetrieveData_CacheIt_ThenReturnElement(
            int rowIndex, int columnIndex)
        {
            // Retrieve a page worth of data containing the requested value.
            CacheObjectItem table = dataSupply.SupplyPageOfData(
                DataPage.MapToLowerBoundary(rowIndex), RowsPerPage);

            // Replace the cached page furthest from the requested cell
            // with a new page containing the newly retrieved data.
            cachePages[GetIndexToUnusedPage(rowIndex)] = new DataPage(table, rowIndex);

            return RetrieveElement(rowIndex, columnIndex);
        }

        // Returns the index of the cached page most distant from the given index
        // and therefore least likely to be reused.
        private int GetIndexToUnusedPage(int rowIndex)
        {
            if (rowIndex > cachePages[0].HighestIndex &&
                rowIndex > cachePages[1].HighestIndex)
            {
                int offsetFromPage0 = rowIndex - cachePages[0].HighestIndex;
                int offsetFromPage1 = rowIndex - cachePages[1].HighestIndex;
                if (offsetFromPage0 < offsetFromPage1)
                {
                    return 1;
                }
                return 0;
            }
            else
            {
                int offsetFromPage0 = cachePages[0].LowestIndex - rowIndex;
                int offsetFromPage1 = cachePages[1].LowestIndex - rowIndex;
                if (offsetFromPage0 < offsetFromPage1)
                {
                    return 1;
                }
                return 0;
            }

        }

        // Returns a value indicating whether the given row index is contained
        // in the given DataPage. 
        private bool IsRowCachedInPage(int pageNumber, int rowIndex)
        {
            return rowIndex <= cachePages[pageNumber].HighestIndex &&
                rowIndex >= cachePages[pageNumber].LowestIndex;
        }
    }

    class CacheObjectList : IList
    {
        private CacheObject memoryCache = null;

        public int Add(object value)
        {
            throw new NotImplementedException();
        }

        public void Clear()
        {
            throw new NotImplementedException();
        }

        public bool Contains(object value)
        {
            throw new NotImplementedException();
        }

        public int IndexOf(object value)
        {
            throw new NotImplementedException();
        }

        public void Insert(int index, object value)
        {
            throw new NotImplementedException();
        }

        public bool IsFixedSize
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsReadOnly
        {
            get { throw new NotImplementedException(); }
        }

        public void Remove(object value)
        {
            throw new NotImplementedException();
        }

        public void RemoveAt(int index)
        {
            throw new NotImplementedException();
        }

        public object this[int index]
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void CopyTo(Array array, int index)
        {
            throw new NotImplementedException();
        }

        public int Count
        {
            get { return memoryCache.Count(); }
        }

        public bool IsSynchronized
        {
            get { throw new NotImplementedException(); }
        }

        public object SyncRoot
        {
            get { throw new NotImplementedException(); }
        }

        public IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }
}




